﻿
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace SourceGenerator.AOP
{
    [Generator]//必须标记该特性，并实现该接口
    public partial class AopProxyGenerator : ISourceGenerator
    {
        //初始化注册刚才的接收器
        public void Initialize(GeneratorInitializationContext context)
        {
#if DEBUG
            //Debugger.Launch(); 
            //使用这个方式，在编译引用该生成器的项目的时候，会进入断点，查看我们写的生成器运行情况
#endif

            context.RegisterForSyntaxNotifications(() => new AopProxySyntaxReceiver());
        }

        public void Execute(GeneratorExecutionContext context)
        {
            var syntaxReceiver = (AopProxySyntaxReceiver)context.SyntaxReceiver;
            var attributeSyntaxList = syntaxReceiver.CandidateClasses;

            if (attributeSyntaxList.Count == 0)
            {
                return;
            }

            List<string> ClassName = new List<string>();

            foreach (AttributeSyntax attributeSyntax in attributeSyntaxList)
            {
                // 找到class，并且判断一下是否有parital字段
                var classDeclarationSyntax = attributeSyntax.FirstAncestorOrSelf<ClassDeclarationSyntax>();
                if (classDeclarationSyntax == null || !classDeclarationSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)))
                {
                    continue;
                }

                //判断是否已经处理过这个class了
                if (ClassName.Contains(classDeclarationSyntax.Identifier.ValueText))
                {
                    continue;
                }

                // 找到namespace
                var namespaceDeclarationSyntax = classDeclarationSyntax.FirstAncestorOrSelf<BaseNamespaceDeclarationSyntax>();

                // 找到methods
                var methodDeclarationList = classDeclarationSyntax.Members.OfType<MethodDeclarationSyntax>().ToList();
                if (methodDeclarationList.Count == 0)
                {
                    continue;
                }

                StringBuilder sb = new StringBuilder();

                sb.AppendLine("using SourceGenerator.AOP;");
                sb.AppendLine("using System.Reflection;");
                sb.AppendLine();

                sb.AppendLine($"namespace {namespaceDeclarationSyntax.Name}");
                sb.AppendLine("{");

                sb.AppendLine($"    public partial class {classDeclarationSyntax.Identifier.Text}");
                sb.AppendLine("    {");

                foreach (MethodDeclarationSyntax methodDeclaration in methodDeclarationList)
                {
                    //判断一下是否包含AOP结尾的特性
                    if (!SyntaxUtils.HasAopAttribute(methodDeclaration, out AttributeSyntax AopAttribute))
                    {
                        continue;
                    }

                    var AopAttributeName = (AopAttribute.Name as IdentifierNameSyntax).Identifier.ValueText;

                    //获取方法的返回值字符串
                    var ReturnTypeStr = (methodDeclaration.ReturnType as PredefinedTypeSyntax).Keyword.ValueText;

                    //获取方法内的参数
                    var ParameterList = methodDeclaration.ParameterList.Parameters;

                    //var asd= ParameterList.Select(s => $"{s.ToFullString().Split(' ')[1]}").ToList();

                    sb.AppendLine("/// <summary>");
                    sb.AppendLine($"/// {methodDeclaration.Identifier.Text}的AOP代理方法");
                    sb.AppendLine($"/// <para>多个AOP特性均会执行</para>");
                    sb.AppendLine("/// </summary>");
                    sb.AppendLine($"public {ReturnTypeStr} {methodDeclaration.Identifier.Text}_Proxy ({string.Join(",", ParameterList.Select(s => $"{s.ToFullString()}"))})");
                    sb.AppendLine("{");

                    if (ReturnTypeStr != "void")
                    {
                        sb.AppendLine($"return ({ReturnTypeStr})ExcuteProxy(\"{methodDeclaration.Identifier.Text}\",{string.Join(",", ParameterList.Select(s => $"{s.ToFullString().Split(' ')[1]}"))});");
                    }
                    else
                    {
                        sb.AppendLine($"ExcuteProxy(\"{methodDeclaration.Identifier.Text}\",{string.Join(",", ParameterList.Select(s => $"{s.ToFullString().Split(' ')[1]}"))});");
                    }

                    sb.AppendLine("}");
                }

                sb.AppendLine("/// <summary>");
                sb.AppendLine("/// 执行代理方法");
                sb.AppendLine("/// </summary>");
                sb.AppendLine($@"


private object ExcuteProxy(string MethodName,params object[] args)
    {{
        object result = null;

        var method = this.GetType().GetMethod(MethodName);
        object[]? attrs = method.GetCustomAttributes(true);

        foreach (var item in attrs)
        {{
            if (item is AopBaseAttribute aop)
            {{
                AopMethod aopMethod = new AopMethod(this, method, args);
                Exception ex = null;
                try
                {{
                    if (aop.Location == InvokeLocation.Before)
                    {{
                        aop.ExcuteBefore();
                    }}

                    if (aop.Location == InvokeLocation.Both)
                    {{
                        aop.ExcuteMiddle(aopMethod);
                    }}
                }}
                catch (Exception exx)
                {{
                    ex=exx;
                }}

                if (aopMethod.ReturnValue is Task)
                {{
                    (aopMethod.ReturnValue as Task).ContinueWith((Task t) =>
                    {{
                        aop.ExcuteAfter(aopMethod, t.Exception);
                    }});
                }}
                else
                {{
                    aop.ExcuteAfter(aopMethod, ex);
                }}

                result = aopMethod.ReturnValue;
            }}
        }}

        return result;
    }}
");

                sb.AppendLine("    }");
                sb.AppendLine("}");

                //使用这个方式格式化代码，免得生成的格式太难看    
                string extensionTextFormatted = CSharpSyntaxTree.ParseText(sb.ToString(), new CSharpParseOptions(LanguageVersion.CSharp8)).GetRoot().NormalizeWhitespace().SyntaxTree.GetText().ToString();
                // 添加到源代码，这样IDE才能感知
                context.AddSource($"{classDeclarationSyntax.Identifier}.g.cs", SourceText.From(extensionTextFormatted, Encoding.UTF8));
                // 保存一下类名，避免重复生成
                ClassName.Add(classDeclarationSyntax.Identifier.ValueText);
            }
        }
    }

}
